package com.lottery.controller;

import com.alibaba.fastjson.JSON;
import com.lottery.dto.SimpleOutputDTO;
import com.lottery.dto.WinningLotteryNumberOutputDTO;
import com.lottery.pojo.*;
import com.lottery.service.OrderService;
import com.lottery.service.RegionService;
import com.lottery.service.WinningHistoriesService;
import com.lottery.service.WinningLotteryNumbersService;
import com.lottery.utils.DateUtils;
import com.lottery.utils.RegionEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@RestController
public class WinningLotteryNumbersController {

    @Autowired
    WinningLotteryNumbersService winningLotteryNumbersService;
    @Autowired
    WinningHistoriesService winningHistoriesService;
    @Autowired
    OrderService orderService;
    @Autowired
    RegionService regionService;

    @PostMapping("saveWinningNumbers")
    public SimpleOutputDTO<String> saveWinningNumbers(@RequestBody List<TableWinningLotteryNumbers> numbers) {
        log.info("saveWinningNumbers(), input:{}", JSON.toJSONString(numbers));
        String dt = DateUtils.getSub4DT();
        String date = DateUtils.getSub4Date();
        //1、迭代保存
        for (TableWinningLotteryNumbers winning_number : numbers) {
            int regionId = winning_number.getRegion_id();
            TableWinningLotteryNumbers winningNum = winningLotteryNumbersService.getByDateRegionId(date, regionId);
            if (winningNum == null) {
                TableWinningLotteryNumbers n = new TableWinningLotteryNumbers();
                n.setRank1_number(winning_number.getRank1_number());
                n.setRank2_number(winning_number.getRank2_number());
                n.setRank3_number(winning_number.getRank3_number());
                n.setChecked_winning(0);
                n.setIs_determined((!winning_number.getRank1_number().equals("") && !winning_number.getRank2_number().equals("") && !winning_number.getRank3_number().equals("")) ? 1 : 0);
                n.setCreated_at(dt);
                n.setUpdated_at(dt);
                n.setDate(date);
                n.setRegion_id(winning_number.getRegion_id());
                winningLotteryNumbersService.addWinningLotteryNumbers(n);
            } else {
                log.info("saveWinningNumbers(), byDateRegionId:{}", JSON.toJSONString(winningNum));
                winningNum.setRank1_number(!winning_number.getRank1_number().equals("") ? winning_number.getRank1_number() : winningNum.getRank1_number());
                winningNum.setRank2_number(!winning_number.getRank2_number().equals("") ? winning_number.getRank2_number() : winningNum.getRank2_number());
                winningNum.setRank3_number(!winning_number.getRank3_number().equals("") ? winning_number.getRank3_number() : winningNum.getRank3_number());
                winningNum.setIs_determined((!winning_number.getRank1_number().equals("") && !winning_number.getRank2_number().equals("") && !winning_number.getRank3_number().equals("")) ? 1 : 0);
                winningNum.setChecked_winning(0);
                log.info("saveWinningNumbers(), 将要保存的winningNum:{}", JSON.toJSONString(winningNum));
                winningLotteryNumbersService.update432(winningNum);
            }
        }
        //2、删除历史
        List<TableWinningHistories> histories = winningHistoriesService.getOrdersByDateRange(null, date + " 00:00:00", date + " 23:59:59");
        log.info("saveWinningNumbers(), 删除history, 开始:{}" + JSON.toJSONString(histories));
        for (TableWinningHistories history : histories) {
            winningHistoriesService.deleteById(history.getId());
        }
        //3、重置orders、bet_numbers
        List<TableOrders> ordersByDate = orderService.getOrdersByDate(0, date + " 00:00:00", date + " 23:59:59");
        log.info("saveWinningNumbers(), 删除order, 开始:{}" + JSON.toJSONString(ordersByDate));
        for (TableOrders o : ordersByDate) {
            //重置bet_numbers
            int orderId = o.getId();
            orderService.updateBet(orderId);
            //重置orders
            orderService.updateOrder(orderId);
        }
        //4、
        List<TableRegions> regions = regionService.getRegions();
        for (TableRegions region : regions) {
            /*//星期几
            int xqj = 0;
            String endX = "end" + xqj;

            //today:2022-04-12*/
            determinePrizesToWinningNumbers(region, date);
        }
        return new SimpleOutputDTO<>("success");
    }

    private void determinePrizesToWinningNumbers(TableRegions region, String date) {
        TableWinningLotteryNumbers number = winningLotteryNumbersService.getNumber(region.getId(), date);
        log.info("determinePrizesToWinningNumbers(), number:{}", JSON.toJSONString(number));
        if (number == null) return;
        // winning numbers
        String rank1_number = number.getRank1_number();
        String rank2_number = number.getRank2_number();
        String rank3_number = number.getRank3_number();
        // General Prizes
        determineGeneralPrizesToWinningNumbers(region, date, rank1_number, 1);
        determineGeneralPrizesToWinningNumbers(region, date, rank2_number, 2);
        determineGeneralPrizesToWinningNumbers(region, date, rank3_number, 3);
        // Grand Prizes
        determineGrandPrizesToWinningNumbers(region, date, rank1_number);
    }

    private void determineGeneralPrizesToWinningNumbers(TableRegions region, String date, String rank_number, int rank) {
        String sub4Date = DateUtils.getSub4Date();
        String startTime = sub4Date + " 00:00:00";
        String endTime = sub4Date + " 23:59:59";
        List<TableOrders> orderByDeletedAndBelong = orderService.getOrderByDeletedAndBelong(region.getRegion_key());
        List<TableBetNumbers> bets4 = new ArrayList<>();
        for (TableOrders order : orderByDeletedAndBelong) {
            bets4.addAll(orderService.getBets432(order.getId(), 0, rank_number, startTime, endTime));
        }
        determineGeneralPrizesToWinningNumbersByDigit(bets4, region, rank, 4);

        List<TableBetNumbers> bets3 = new ArrayList<>();
        for (TableOrders order : orderByDeletedAndBelong) {
            bets3.addAll(orderService.getBets432(order.getId(), 0, rank_number.substring(1, 4), startTime, endTime));
        }
        determineGeneralPrizesToWinningNumbersByDigit(bets3, region, rank, 3);

        List<TableBetNumbers> bets2 = new ArrayList<>();
        for (TableOrders order : orderByDeletedAndBelong) {
            bets2.addAll(orderService.getBets432(order.getId(), 0, rank_number.substring(2, 4), startTime, endTime));
        }
        determineGeneralPrizesToWinningNumbersByDigit(bets2, region, rank, 2);
    }

    private void determineGeneralPrizesToWinningNumbersByDigit(List<TableBetNumbers> numbers, TableRegions region, int rank, int digit) {
        for (TableBetNumbers number : numbers) {
            //更新bet_numbers
            number.setIs_winner(1);
            number.setRank(rank);
            double generalPrizes = getGeneralPrizes(number.getBet_number(), rank, number.getBet_money());
            number.setPrizes(number.getPrizes() + generalPrizes);
            number.setWinning_payment_time(DateUtils.getSub4DT());
            orderService.updateBet432(number.getOrder_id(), number.getRank(), number.getPrizes(), number.getWinning_payment_time());
            //更新orders
            TableOrders order = orderService.getOrderById(number.getOrder_id());
            order.setTotal_winning_amount(order.getTotal_winning_amount() + generalPrizes);
            order.setIs_winner(1);
            orderService.updateOrder432(order.getId(), order.getTotal_winning_amount());
            //winning history
            List<TableWinningHistories> histories = winningHistoriesService.getByBetNumberId(number.getId());
            if (histories == null || histories.size() < 1) {
                TableWinningHistories history = new TableWinningHistories();
                history.setBet_number_id(number.getId());
                history.setBet_number(number.getBet_number());
                history.setMachine_number(order.getMachine_number());
                history.setSerial_number(order.getSerial_number());
                history.setOrder_id(number.getOrder_id());
                history.setOrder_id(region.getId());
                history.setRank(rank);
                history.setDigit(digit);
                history.setCreated_at(DateUtils.getSub4DT());
                winningHistoriesService.add(history);
            } else {
                winningHistoriesService.update(number.getId(), "general", generalPrizes);
            }

        }
    }

    private void determineGrandPrizesToWinningNumbers(TableRegions region, String date, String rank_number) {
        String sub4Date = DateUtils.getSub4Date();
        String startTime = sub4Date + " 00:00:00";
        String endTime = sub4Date + " 23:59:59";
        List<TableOrders> orderByDeletedAndBelong = orderService.getOrderByDeletedAndBelong(region.getRegion_key());
        List<TableBetNumbers> bets4 = new ArrayList<>();
        for (TableOrders order : orderByDeletedAndBelong) {
            bets4.addAll(orderService.getBets432(order.getId(), 1, rank_number + " P", startTime, endTime));
        }
        determineGrandPrizesToWinningNumbersByDigit(bets4, region, 4);

        List<TableBetNumbers> bets3 = new ArrayList<>();
        for (TableOrders order : orderByDeletedAndBelong) {
            bets3.addAll(orderService.getBets432(order.getId(), 1, rank_number.substring(1, 4) + " P", startTime, endTime));
        }
        determineGrandPrizesToWinningNumbersByDigit(bets3, region, 3);

        List<TableBetNumbers> bets2 = new ArrayList<>();
        for (TableOrders order : orderByDeletedAndBelong) {
            bets2.addAll(orderService.getBets432(order.getId(), 1, rank_number.substring(2, 4) + " P", startTime, endTime));
        }
        determineGrandPrizesToWinningNumbersByDigit(bets2, region, 2);
    }

    private void determineGrandPrizesToWinningNumbersByDigit(List<TableBetNumbers> numbers, TableRegions region, int digit) {
        for (TableBetNumbers number : numbers) {
            //更新bet_numbers
            number.setIs_winner(1);
            number.setRank(1);
            double grandPrizes = getGrandPrizes(digit, number.getBet_money());
            number.setPrizes(number.getPrizes() + grandPrizes);
            number.setWinning_payment_time(DateUtils.getSub4DT());
            orderService.updateBet432(number.getOrder_id(), number.getRank(), number.getPrizes(), number.getWinning_payment_time());
            //更新orders
            TableOrders order = orderService.getOrderById(number.getOrder_id());
            order.setTotal_winning_amount(order.getTotal_winning_amount() + grandPrizes);
            order.setIs_winner(1);
            orderService.updateOrder432(order.getId(), order.getTotal_winning_amount());
            //winning history
            List<TableWinningHistories> histories = winningHistoriesService.getByBetNumberId(number.getId());
            if (histories == null || histories.size() < 1) {
                TableWinningHistories history = new TableWinningHistories();
                history.setBet_number_id(number.getId());
                history.setBet_number(number.getBet_number());
                history.setMachine_number(order.getMachine_number());
                history.setSerial_number(order.getSerial_number());
                history.setOrder_id(number.getOrder_id());
                history.setOrder_id(region.getId());
                history.setRank(1);
                history.setDigit(digit);
                history.setCreated_at(DateUtils.getSub4DT());
                winningHistoriesService.add(history);
            } else {
                winningHistoriesService.update(number.getId(), "grand", grandPrizes);
            }

        }
    }

    private double getGrandPrizes(int digit, double money) {
        double amount = 0;
        int[] digit4Num = new int[]{3000, 1500, 750};
        int[] digit3Num = new int[]{400, 200, 100};
        int[] digit2Num = new int[]{40, 20, 10};
        switch (digit) {
            case 4:
                amount = digit4Num[0] + digit4Num[1] + digit4Num[2];
                break;
            case 3:
                amount = digit3Num[0] + digit3Num[1] + digit3Num[2];
                break;
            case 2:
                amount = digit2Num[0] + digit2Num[1] + digit2Num[2];
                break;
        }

        return amount * money;
    }

    private double getGeneralPrizes(String betNumber, int rank, double money) {
        double amount = 0;
        int[] digit4Num = new int[]{3000, 1500, 750};
        int[] digit3Num = new int[]{400, 200, 100};
        int[] digit2Num = new int[]{40, 20, 10};
        switch (betNumber.length()) {
            case 4:
                amount = digit4Num[rank - 1];
                break;
            case 3:
                amount = digit3Num[rank - 1];
                break;
            case 2:
                amount = digit2Num[rank - 1];
                break;
        }

        return amount * money;
    }


    ///=========================
    @PostMapping("addWinningLotteryNumbers")
    public SimpleOutputDTO<String> addWinningLotteryNumbers(@RequestBody List<TableWinningLotteryNumbers> numbers) {
        SimpleOutputDTO<String> outputDTO = new SimpleOutputDTO<>();
        log.info("addWinningLotteryNumbers(), numbers:" + numbers.toString());

        List<WinningLotteryNumberOutputDTO> dbNumbers = getWinningLotteryNumbers(DateUtils.nowDate(), DateUtils.nowDate(), null);
        log.info("addWinningLotteryNumbers(), dbNumbers:" + dbNumbers.toString() + ", nowDate:" + DateUtils.nowDate());
        if (dbNumbers.size() > 0) {
            for (TableWinningLotteryNumbers number : numbers) {
                number.setDate(DateUtils.nowDate());
                number.setIs_determined(0);
                number.setChecked_winning(0);
                String nowDatetime = DateUtils.getSub4DT();
                number.setCreated_at(nowDatetime);
                number.setUpdated_at(nowDatetime);
                winningLotteryNumbersService.updateWinningLotteryNumbers(number);
            }
            outputDTO.setData("更新成功");

            if (dbNumbers.get(0).getKNumber().getChecked_winning() != 0) {
                outputDTO.setData("已有兑奖订单，不可设置！");
                return outputDTO;
            }
        } else {
            outputDTO.setData("添加成功");
            for (TableWinningLotteryNumbers number : numbers) {
                number.setDate(DateUtils.nowDate());
                number.setIs_determined(0);
                number.setChecked_winning(0);
                String nowDatetime = DateUtils.getSub4DT();
                number.setCreated_at(nowDatetime);
                number.setUpdated_at(nowDatetime);
                winningLotteryNumbersService.addWinningLotteryNumbers(number);
            }
        }
        return outputDTO;
    }


    /**
     * [date]localhost:8001/getWinningLotteryNumbersByDate?date=2021-05-02
     * 这个接口较为耗时0.5d
     */
    @PostMapping("getWinningLotteryNumbers")
    public List<WinningLotteryNumberOutputDTO> getWinningLotteryNumbers(
            String startTime, String endTime,
            @RequestParam(value = "before", required = false) Integer before
    ) {
        List<WinningLotteryNumberOutputDTO> list = new ArrayList<>();

        String sqlStartTime = null;
        String sqlEndTime = null;
        if (before != null && before > 0) {
            sqlStartTime = DateUtils.getBeforeDate(before);
            sqlEndTime = DateUtils.nowDate();
        } else {
            sqlStartTime = startTime;
            sqlEndTime = endTime;
        }
        log.info("getWinningLotteryNumbersByDate(), sqlStartTime:" + sqlStartTime + ", sqlEndTime:" + sqlEndTime + ", before:" + before);

        List<TableWinningLotteryNumbers> numbers = winningLotteryNumbersService.getWinningLotteryNumbers(sqlStartTime, sqlEndTime);
        //按date分组
        Map<String, List<TableWinningLotteryNumbers>> collect = numbers.stream().collect(Collectors.groupingBy(new Function<TableWinningLotteryNumbers, String>() {
            @Override
            public String apply(TableWinningLotteryNumbers tableWinningLotteryNumbers) {
                return tableWinningLotteryNumbers.getDate();
            }
        }));
        //LogUtils.info(getClass().getSimpleName(), "collect:" + collect.toString());
        for (String key : collect.keySet()) {
            WinningLotteryNumberOutputDTO dto = new WinningLotteryNumberOutputDTO();
            List<TableWinningLotteryNumbers> tableWinningLotteryNumbers = collect.get(key);
            //LogUtils.info(getClass().getSimpleName(), "tableWinningLotteryNumbers:" + tableWinningLotteryNumbers);
            for (TableWinningLotteryNumbers number : tableWinningLotteryNumbers) {
                dto.setDate(number.getDate());
                int regionId = number.getRegion_id();
                if (regionId == RegionEnum.KORSOU.getId()) {
                    dto.setKNumber(number);
                } else if (regionId == RegionEnum.FLAMINGO.getId()) {
                    dto.setFNumber(number);
                } else if (regionId == RegionEnum.DIARIO.getId()) {
                    dto.setDNumber(number);
                } else if (regionId == RegionEnum.ST.getId()) {
                    dto.setSNumber(number);
                }
            }
            list.add(dto);
        }

        //按date排序
        list.sort(new Comparator<WinningLotteryNumberOutputDTO>() {
            @Override
            public int compare(WinningLotteryNumberOutputDTO o1, WinningLotteryNumberOutputDTO o2) {
                return o2.getDate().compareTo(o1.getDate());
            }
        });
        return list;
    }
}
